//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////


#include "precomp.h"

//
// CallerID, DialedID, Closed User Group status values
//
static const DWORD g_rgdwSupSvcStats[] =
{
    RIL_SVCSTAT_DISABLED,           // 0
    RIL_SVCSTAT_ENABLED,            // 1
};
#define NUM_SUPSVCSTATS     (sizeof(g_rgdwSupSvcStats) / sizeof(DWORD))


//
// CallerID and DialedID provisioning values
//
static const DWORD g_rgdwSupSvcProv[] =
{
    RIL_SVCPROV_NOTPROVISIONED,     // 0
    RIL_SVCPROV_PROVISIONED,        // 1
    RIL_SVCPROV_UNKNOWN,            // 2
};
#define NUM_SUPSVCPROV      (sizeof(g_rgdwSupSvcProv) / sizeof(DWORD))


//
// HideID status values
//
static const DWORD g_rgdwHideIDStats[] =
{
    RIL_SVCSTAT_DEFAULT,            // 0
    RIL_SVCSTAT_ENABLED,            // 1
    RIL_SVCSTAT_DISABLED,           // 2
};
#define NUM_HIDEIDSTATS   (sizeof(g_rgdwHideIDStats) / sizeof(DWORD))


//
// HideID provisioning values
//
static const DWORD g_rgdwHideIDProv[] =
{
    RIL_SVCPROV_NOTPROVISIONED,     // 0
    RIL_SVCPROV_PROVISIONED,        // 1
    RIL_SVCPROV_UNKNOWN,            // 2
    RIL_SVCPROV_TEMPMODERESTRICTED, // 3
    RIL_SVCPROV_TEMPMODEALLOWED,    // 4
};
#define NUM_HIDEIDPROV   (sizeof(g_rgdwHideIDProv) / sizeof(DWORD))


//
// Closed User Group info values
//
static const DWORD g_rgdwClosedGroupInfos[] =
{
    RIL_CUGINFO_NONE,               // 0
    RIL_CUGINFO_SUPPRESSOA,         // 1
    RIL_CUGINFO_SUPRESSPREF,        // 2
    RIL_CUGINFO_SUPPRESSOAANDPREF,  // 3
};
#define NUM_CLOSEDGROUPINFOS    (sizeof(g_rgdwClosedGroupInfos) / sizeof(DWORD))


//
// Call Forwarding Reasons
//
static const DWORD g_rgdwFwdReasons[] =
{
    RIL_FWDREASON_UNCONDITIONAL,    // 0
    RIL_FWDREASON_MOBILEBUSY,       // 1
    RIL_FWDREASON_NOREPLY,          // 2
    RIL_FWDREASON_UNREACHABLE,      // 3
    RIL_FWDREASON_ALLFORWARDING,    // 4
    RIL_FWDREASON_ALLCONDITIONAL,   // 5
};
#define NUM_FWDREASONS          (sizeof(g_rgdwFwdReasons) / sizeof(DWORD))

static BOOL g_fAllowEnableHideCallerId = TRUE;
int g_iHideCallerId = RIL_SVCPROV_UNKNOWN;
DWORD g_dwLastCUSSDCmd = 0;
extern BOOL g_fCallWaiting;
extern BOOL g_fDisableCOLPInformation;
extern UINT g_nCOLPStatus;
extern char g_szSequenceFromDialer[MAX_ATCMD_LEN];

// Translation InfoClass (Microsoft) -> BS-Code (GSM)
// Parameter:
// dwInfoClass - InfoClass (Microsoft)
// Return Value:
// 0 = translation not possible
// (other) - BS Code (GSM)
UINT RILDrv_FindBS4InfoClass(DWORD dwInfoClass)
{
    UINT uiBS = 0;
    UINT i;

    for (i = 0; i < NUM_BS_VALUES; i++)
    {
        if (g_rgInfoClassTable[i].dwInfoClass == dwInfoClass)
        {
            uiBS = g_rgInfoClassTable[i].uiBS;
        }
    }
    return uiBS;
}

//
//
//
static HRESULT ParseGetCallerIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallerIdSettings);
    UINT nValue;
    RILCALLERIDSETTINGS* prcids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcids = (RILCALLERIDSETTINGS*)AllocBlob(sizeof(RILCALLERIDSETTINGS));
    if (!prcids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcids, 0x00, sizeof(RILCALLERIDSETTINGS));
    prcids->cbSize = sizeof(RILCALLERIDSETTINGS);

    // Parse "<prefix>+CLIP: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CLIP: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_SUPSVCSTATS > nValue) {
        prcids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prcids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    // MC75: since we do not use CLIP we use the simulated CLIP value instead.
    if (g_dwModemType == MODEMTYPE_SIEMENS_MC75)
        prcids->dwStatus = ((DWORD)pParam == 1 ? RIL_SVCSTAT_ENABLED : RIL_SVCSTAT_DISABLED);
    prcids->dwParams |= RIL_PARAM_CIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCPROV > nValue) {
        prcids->dwProvisioning = g_rgdwSupSvcProv[nValue];
    } else {
        prcids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prcids->dwParams |= RIL_PARAM_CIDS_PROVISIONING;

    // Workaround: the Microsoft MMI displays dwStatus when it should display dwProvisioning.
    if (prcids->dwProvisioning == RIL_SVCPROV_UNKNOWN)
        prcids->dwStatus = RIL_SVCSTAT_UNKNOWN;

    pBlob = (void*)prcids;
    cbBlob = sizeof(RILCALLERIDSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcids);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCallerIdSettings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetCallerIdSettings);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(URC_PORT, pHandle, "AT+CLIP?\r", CMDOPT_NONE, APIID_GETCALLERIDSETTINGS, ParseGetCallerIdSettings, NULL, hr, NULL, (LPVOID)pHandle->GetDevice()->IsSimulatedCLIPActive())) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCallerIdStatus(DWORD dwParam, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetCallerIdStatus);
    UINT i;
    UINT nValue;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)dwParam;
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (g_dwModemType == MODEMTYPE_SIEMENS_MC75)
    {
        // We don't use AT+CLIP on the MC75.
        pHandle->GetDevice()->SetSimulatedCLIPActive(nValue);
        if (!QueueCmd(URC_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_SETCALLERIDSTATUS, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CLIP=%d\r", nValue);
        if (!QueueCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETCALLERIDSTATUS, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetHideIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetHideIdSettings);
    UINT nValue;
    RILHIDEIDSETTINGS* prhids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prhids = (RILHIDEIDSETTINGS*)AllocBlob(sizeof(RILHIDEIDSETTINGS));
    if (!prhids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prhids, 0x00, sizeof(RILHIDEIDSETTINGS));
    prhids->cbSize = sizeof(RILHIDEIDSETTINGS);

    // Parse "<prefix>+CLIR: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CLIR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_HIDEIDSTATS > nValue) {
        prhids->dwStatus = g_rgdwHideIDStats[nValue];
    } else {
        prhids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    if (g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
    {
        prhids->dwStatus = g_iHideCallerId;
    }
    else if (g_iHideCallerId != RIL_SVCPROV_UNKNOWN && (prhids->dwStatus == RIL_SVCSTAT_UNKNOWN || prhids->dwStatus == RIL_SVCSTAT_DEFAULT))
    {
        prhids->dwStatus = g_iHideCallerId;
    }
    if (prhids->dwStatus == RIL_SVCSTAT_UNKNOWN)
        prhids->dwStatus = RIL_SVCSTAT_DEFAULT;
    prhids->dwParams |= RIL_PARAM_HIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_HIDEIDPROV > nValue) {
        prhids->dwProvisioning = g_rgdwHideIDProv[nValue];
    } else {
        prhids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prhids->dwParams |= RIL_PARAM_HIDS_PROVISIONING;

    if (RIL_SVCPROV_NOTPROVISIONED == prhids->dwProvisioning) {
        g_fAllowEnableHideCallerId = FALSE;
    } else {
        g_fAllowEnableHideCallerId = TRUE;
    }

    pBlob = (void*)prhids;
    cbBlob = sizeof(RILHIDEIDSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prhids);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetHideIdSettings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetHideIdSettings);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, (g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46?"ATD*#31#;\r":"AT+CLIR?\r"), CMDOPT_NONE, APIID_GETHIDEIDSETTINGS, ParseGetHideIdSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetHideIdStatus(DWORD dwParam, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetHideIdStatus);
    UINT i;
    UINT nValue;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    //do not allow CLIR invocation if it is not provisioned
    if ((RIL_SVCSTAT_ENABLED == dwStatus) && !g_fAllowEnableHideCallerId) {
        hr = RIL_E_SVCNOTSUBSCRIBED;
        goto Error;
    }

    for (i = 0; i < NUM_HIDEIDSTATS; i++) {
        if (dwStatus == g_rgdwHideIDStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_HIDEIDSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    g_iHideCallerId = dwStatus;
    if(g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT\r");
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CLIR=%u\r", nValue);
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETHIDEIDSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetDialedIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetDialedIdSettings);
    UINT nValue;
    RILDIALEDIDSETTINGS* prdids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prdids = (RILDIALEDIDSETTINGS*)AllocBlob(sizeof(RILDIALEDIDSETTINGS));
    if (!prdids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prdids, 0x00, sizeof(RILDIALEDIDSETTINGS));
    prdids->cbSize = sizeof(RILDIALEDIDSETTINGS);

    // Parse "<prefix>+COLP: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+COLP: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_fDisableCOLPInformation)      // COLP is supressed, so we have to use the stored value.
        nValue = g_nCOLPStatus;

    if (NUM_SUPSVCSTATS > nValue) {
        prdids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prdids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCPROV > nValue) {
        prdids->dwProvisioning = g_rgdwSupSvcProv[nValue];
    } else {
        prdids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_PROVISIONING;

    pBlob = (void*)prdids;
    cbBlob = sizeof(RILDIALEDIDSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prdids);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetDialedIdSettings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetDialedIdSettings);
    HRESULT hr = S_OK;
    char szCmd[MAX_ATCMD_LEN];
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if(g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
    {
        strcpy(szCmd, "ATD*#76#;\r");
    }
    else
    {
        strcpy(szCmd, "AT+COLP?\r");
    }
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_GETDIALEDIDSETTINGS, ParseGetDialedIdSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetDialedIdStatus(DWORD dwParam, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetDialedIdStatus);
    UINT i;
    UINT nValue;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)dwParam;
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if(g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
        return E_NOTIMPL;

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // If COLP is disabled via registry key we must block the activation here.
    g_nCOLPStatus = nValue;
    if (g_fDisableCOLPInformation && dwStatus == RIL_SVCSTAT_ENABLED)
        nValue = 0;

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COLP=%d\r", nValue);
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETDIALEDIDSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetHideConnectedIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetDialedIdSettings);
    UINT nValue;
    RILHIDECONNECTEDIDSETTINGS* prdids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prdids = (RILHIDECONNECTEDIDSETTINGS*)AllocBlob(sizeof(RILHIDECONNECTEDIDSETTINGS));
    if (!prdids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prdids, 0x00, sizeof(RILDIALEDIDSETTINGS));
    prdids->cbSize = sizeof(RILDIALEDIDSETTINGS);

    // Parse "<prefix>+COLR: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+COLR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_PARAM_DIDS_STATUS
    // In case a future module supports the actual AT+COLR command and not only ATD*#77#;
    // we should include this code.
    if (NUM_SUPSVCSTATS > nValue) {
        prdids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prdids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_STATUS;
#endif

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCPROV > nValue) {
        prdids->dwProvisioning = g_rgdwSupSvcProv[nValue];
    } else {
        prdids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_PROVISIONING;

    pBlob = (void*)prdids;
    cbBlob = sizeof(RILHIDECONNECTEDIDSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prdids);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetHideConnectedIdSettings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetDialedIdSettings);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, "ATD*#77#;\r", CMDOPT_NONE, APIID_GETHIDECONNECTEDIDSETTINGS, ParseGetHideConnectedIdSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetClosedGroupSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetClosedGroupSettings);
    UINT nValue;
    RILCLOSEDGROUPSETTINGS* prcgs = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcgs = (RILCLOSEDGROUPSETTINGS*)AllocBlob(sizeof(RILCLOSEDGROUPSETTINGS));
    if (!prcgs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcgs, 0x00, sizeof(RILCLOSEDGROUPSETTINGS));
    prcgs->cbSize = sizeof(RILCLOSEDGROUPSETTINGS);

    // Parse "<prefix>+CCUG: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CCUG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCSTATS > nValue) {
        prcgs->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prcgs->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_STATUS;

    // Parse ",<index>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 11, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    if (10 == nValue) {
        // Special index value that means "no index"
        prcgs->dwIndex = RIL_CUGINDEX_NONE;
    } else {
        prcgs->dwIndex = nValue;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_INDEX;

    // Parse ",<info><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_CLOSEDGROUPINFOS > nValue) {
        prcgs->dwInfo = g_rgdwClosedGroupInfos[nValue];
    } else {
        prcgs->dwInfo = RIL_CUGINFO_NONE;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_INFO;

    pBlob = (void*)prcgs;
    cbBlob = sizeof(RILCLOSEDGROUPSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcgs);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetClosedGroupSettings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetClosedGroupSettings);

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CCUG?\r", CMDOPT_NONE, APIID_GETCLOSEDGROUPSETTINGS, ParseGetClosedGroupSettings,
                  NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:

    return hr;
}


//
//
//
HRESULT RILDrv_SetClosedGroupSettings(DWORD dwParam, const RILCLOSEDGROUPSETTINGS* lpSettings)
{
    TBD_FUNCTION(RILDrv_SetClosedGroupSettings);

    TBD_ASSERT(NULL != lpSettings);

    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    UINT i;
    UINT nValue;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpSettings->dwParams & RIL_PARAM_CGS_STATUS)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (lpSettings->dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "AT+CCUG=%u", nValue);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    if (lpSettings->dwParams & RIL_PARAM_CGS_INDEX) {
        nValue = (RIL_CUGINDEX_NONE == lpSettings->dwIndex ? 10 : lpSettings->dwIndex);
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    if (lpSettings->dwParams & RIL_PARAM_CGS_INFO) {
        for (i = 0; i < NUM_CLOSEDGROUPINFOS; i++) {
            if (lpSettings->dwInfo == g_rgdwClosedGroupInfos[i]) {
                nValue = i;
                break;
            }
        }
        if (NUM_CLOSEDGROUPINFOS == i) {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETCLOSEDGROUPSETTINGS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:

    return hr;
}


//
//
//
static HRESULT ParseGetCallForwardingSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallForwardingSettings);
    UINT i;
    UINT nValue;
    char szAddress[MAXLENGTH_ADDRESS];
#ifdef ETSI_SPEC
    char szSubAddress[MAXLENGTH_SUBADDR];
#endif
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCALLFORWARDINGSETTINGS* rgrcfs = NULL;
    BOOL fSubAddressPresent = FALSE;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CCFC: "
    while (MatchStringBeginning(szRsp, "+CCFC: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcfs, sizeof(RILCALLFORWARDINGSETTINGS), nUsed, &nAllocated, SUPSVC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        (void)memset(&rgrcfs[nUsed], 0x00, sizeof(RILCALLFORWARDINGSETTINGS));
        rgrcfs[nUsed].cbSize = sizeof(RILCALLFORWARDINGSETTINGS);

        // Parse "<status>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        if (NUM_SUPSVCSTATS > nValue) {
            rgrcfs[nUsed].dwStatus = g_rgdwSupSvcStats[nValue];
        } else {
            rgrcfs[nUsed].dwStatus = RIL_SVCSTAT_UNKNOWN;
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_STATUS;

        // Parse ",<class>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (nValue & (0x01 << i)) {
                rgrcfs[nUsed].dwInfoClasses |= g_rgdwInfoClasses[i];
            }
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_INFOCLASSES;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<address>,<type>"
            if (!ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
                !MatchStringBeginning(szRsp, ",", szRsp)                     ||
                !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
                !StringToRILAddress(szAddress, (BYTE)nValue, rgrcfs[nUsed].raAddress)) {
                goto Continue;
            }
            rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_ADDRESS;
            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<subaddr>,<subaddr_type>"
#ifdef ETSI_SPEC
                if (ParseString(szRsp, szSubAddress, MAXLENGTH_SUBADDR, szRsp)) {
                    fSubAddressPresent = TRUE;
                }

                // Parse ","
                if (!MatchStringBeginning(szRsp, ",", szRsp)) {
                    goto Continue;
                }

                // Parse "<subaddr_type>"
                if (ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) &&
                    fSubAddressPresent                                          &&
                    StringToRILSubAddress(szSubAddress, (BYTE)nValue, rgrcfs[nUsed].rsaSubAddress)) {
                    rgrcfs[nUsed].dwParams |= RIL_PARAM_RPI_SUBADDRESS;
                }

                // Parse ","
                if (MatchStringBeginning(szRsp, ",", szRsp)) {
                    // Parse "<time>
                    if (!ParseUInt(szRsp, FALSE, nValue, szRsp)) {
                        goto Continue;
                    }
                    rgrcfs[nUsed].dwDelayTime = nValue;
                    rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_DELAYTIME;
                }
#else
                if (!ParseUInt(szRsp, FALSE, nValue, szRsp)) {
                    goto Continue;
                }
                rgrcfs[nUsed].dwDelayTime = nValue;
                rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_DELAYTIME;
#endif
            }
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrcfs;
    cbBlob = nUsed * sizeof(RILCALLFORWARDINGSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(rgrcfs);
    }
    return hr;
}

//
// Siemens specific: if ATD*# is used instead of AT+CCFC, the responses look different
//
static HRESULT ParseGetCallForwardingSettingsATD(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallForwardingSettings);
    UINT i;
    UINT nValue;
    char szAddress[MAXLENGTH_ADDRESS];
#ifdef ETSI_SPEC
    char szSubAddress[MAXLENGTH_SUBADDR];
#endif
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCALLFORWARDINGSETTINGS* rgrcfs = NULL;
    BOOL fSubAddressPresent = FALSE;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "^SCCFC: "
    while (MatchStringBeginning(szRsp, "^SCCFC: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcfs, sizeof(RILCALLFORWARDINGSETTINGS), nUsed, &nAllocated, SUPSVC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        (void)memset(&rgrcfs[nUsed], 0x00, sizeof(RILCALLFORWARDINGSETTINGS));
        rgrcfs[nUsed].cbSize = sizeof(RILCALLFORWARDINGSETTINGS);

        // Parse <reason> (not needed)
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            goto Continue;
        }

        // Parse "<status>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        if (NUM_SUPSVCSTATS > nValue) {
            rgrcfs[nUsed].dwStatus = g_rgdwSupSvcStats[nValue];
        } else {
            rgrcfs[nUsed].dwStatus = RIL_SVCSTAT_UNKNOWN;
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_STATUS;

        // Parse ",<class>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (nValue & (0x01 << i)) {
                rgrcfs[nUsed].dwInfoClasses |= g_rgdwInfoClasses[i];
            }
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_INFOCLASSES;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<address>,<type>"
            if (!ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
                !MatchStringBeginning(szRsp, ",", szRsp)                     ||
                !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
                !StringToRILAddress(szAddress, (BYTE)nValue, rgrcfs[nUsed].raAddress)) {
                goto Continue;
            }
            rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_ADDRESS;
            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<subaddr>,<subaddr_type>"
                if (!ParseUInt(szRsp, FALSE, nValue, szRsp)) {
                    goto Continue;
                }
                rgrcfs[nUsed].dwDelayTime = nValue;
                rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_DELAYTIME;
            }
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrcfs;
    cbBlob = nUsed * sizeof(RILCALLFORWARDINGSETTINGS);

Error:
    if (FAILED(hr)) {
        FreeBlob(rgrcfs);
    }
    return hr;
}


// Generation of an ATD-*#-Code instead of AT+CCFC (if possible)
// Parameters: see RILDrv_GetCallForwardingSettings()
// Return Value:
// TRUE - ATD-string created
// FALSE - No corroponding ATD available
BOOL RILDrv_GenerateATDString4CCFC(DWORD dwReason, DWORD dwInfoClass, LPSTR lpszATDString, DWORD dwStatus, LPCSTR lpcstrDN, DWORD dwDelayTime)
{
    BOOL bATDavailable = FALSE;
    UINT uiBS;

    uiBS = RILDrv_FindBS4InfoClass(dwInfoClass);
    if (uiBS > 0)
    {
        UINT i;

        for (i = 0; i < NUM_FORWARDING_VALUES; i++)
        {
            if (g_rgForwardingTypeTable[i].dwForwardingReason == dwReason && g_rgForwardingTypeTable[i].dwForwardingStatus == dwStatus)
            {
                char szBS[6];

                if (uiBS >= 100)
                    szBS[0] = '\0';
                else
                    _snprintfz(szBS, 6, "%d", uiBS);

                if (!g_rgForwardingTypeTable[i].bNeedsT)
                {
                    (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s#;\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS);
                }
                else
                {
                    if (dwDelayTime == 0)
                        (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s*#;\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS);
                    else
                        (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s*%d#;\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS, dwDelayTime);
                }
                bATDavailable = TRUE;
                break;
            }
        }
    }
    return bATDavailable;
}

BOOL GetDelayTimeFromSequenceString(DWORD dwReason, BOOL &bDelayTime, DWORD &dwDelayTime)
{
    BOOL bFoundInString = FALSE;

    if (strlen(g_szSequenceFromDialer) > 4)
    {
        if ((dwReason == RIL_FWDREASON_ALLFORWARDING && strncmp(g_szSequenceFromDialer, "**002*", 6) == 0) ||
             (dwReason == RIL_FWDREASON_ALLCONDITIONAL && strncmp(g_szSequenceFromDialer, "**004*", 6) == 0) ||
             (dwReason == RIL_FWDREASON_NOREPLY && strncmp(g_szSequenceFromDialer, "**61*", 5) == 0))
        {
            int iToken;
            char *szToken;

            iToken = 0;
            szToken = strtok(g_szSequenceFromDialer+2, "*");
            while (szToken != NULL)
            {
                if (iToken == 2)
                {
                    bDelayTime = FALSE;
                    dwDelayTime = 0;
                    bFoundInString = TRUE;
                }
                if (iToken == 3 && atoi(szToken) > 0)
                {
                    bDelayTime = TRUE;
                    dwDelayTime = atoi(szToken);
                }
                szToken = strtok(NULL, "*");
                iToken ++;
            }
        }
    }

    g_szSequenceFromDialer[0] = 0;

    return bFoundInString;
 }

//
//
//
HRESULT RILDrv_GetCallForwardingSettings(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses)
{
    TBD_FUNCTION(RILDrv_GetCallForwardingSettings);
    UINT i;
    UINT nValue;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCFC
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_QUERY, "", 0))
    {
#endif
        for (i = 0; i < NUM_FWDREASONS; i++) {
            if (dwReason == g_rgdwFwdReasons[i]) {
                nValue = i;
                break;
            }
        }
        if (NUM_FWDREASONS == i) {
            hr = E_INVALIDARG;
            goto Error;
        }

        szWalk = BeginLineSpecificCommand(szCmd, MAX_ATCMD_LEN, 0);

        if (dwInfoClasses == (RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX)) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,2\r", nValue);
        }
        else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,2,,,%u\r", nValue, dwClass);
        }

        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_GETCALLFORWARDINGSETTINGS, ParseGetCallForwardingSettings, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
#ifndef RIL_USE_GSM0707_CMDS
    }
    else
    {
        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_GETCALLFORWARDINGSETTINGS, ParseGetCallForwardingSettingsATD, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
#endif

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_AddCallForwarding(DWORD dwParam, DWORD dwReason, const RILCALLFORWARDINGSETTINGS* lpSettings)
{
    TBD_FUNCTION(RILDrv_AddCallForwarding);
    UINT i;
    DWORD dwInfoClasses;
    DWORD dwDelayTime = 0;
    BOOL bDelayTime = FALSE;
    UINT nValue;
    DWORD dwClass = 0;
    BYTE bTypeOfAddress;
    char szAddress[MAXLENGTH_ADDRESS];
    char szCmd[SUPSVC_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Address and information classes must be specified
    if (!(lpSettings->dwParams & RIL_PARAM_CFS_ADDRESS) ||
        !(lpSettings->dwParams & RIL_PARAM_CFS_INFOCLASSES)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (!GetDelayTimeFromSequenceString(dwReason, bDelayTime, dwDelayTime))
    {
        if(lpSettings->dwParams & RIL_PARAM_CFS_DELAYTIME)
        {
            bDelayTime = TRUE;
            dwDelayTime = lpSettings->dwDelayTime;
        }
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(lpSettings->raAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }

    dwInfoClasses = lpSettings->dwInfoClasses;
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCFC
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_REGISTER, szAddress, dwDelayTime))
    {
#endif
        szWalk = BeginLineSpecificCommand(szCmd, SUPSVC_CMDBUF_LENGTH, 0);

        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                dwClass |= (0x01 << i);
            }
        }

        (void)_snprintfz(szWalk, SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd), "+CCFC=%u,3,\"%s\",%u,%u", nValue, szAddress,
                         bTypeOfAddress, dwClass);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        // Add delay time
        if (bDelayTime && ((RIL_FWDREASON_NOREPLY == dwReason) || // GSM 07.07: <time>: when "no reply" is enabled or queried...
                                     (RIL_FWDREASON_ALLFORWARDING == dwReason) ||
                                     (RIL_FWDREASON_ALLCONDITIONAL == dwReason))) {
            // HJREVIEW - MC45 does not support the extra comma's in command string
            (void)_snprintfz(szWalk, SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd), ",%u", dwDelayTime);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        (void)strncpyz(szWalk, "\r", SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_ADDCALLFORWARDING, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_RemoveCallForwarding(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses)
{
    TBD_FUNCTION(RILDrv_RemoveCallForwarding);
    UINT i;
    UINT nValue;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCFC
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_REMOVE, "", 0))
    {
#endif
        szWalk = BeginLineSpecificCommand(szCmd, MAX_ATCMD_LEN, 0);

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,4", nValue);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        } else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,4,,,%u", nValue, dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_REMOVECALLFORWARDING, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCallForwardingStatus(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetCallForwardingStatus);
    UINT i;
    UINT nValue;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }


    // Check thet the stsatus value is in the correct range
    if (RIL_SVCSTAT_DISABLED != dwStatus && RIL_SVCSTAT_ENABLED != dwStatus) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCFC
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, (RIL_SVCSTAT_DISABLED == dwStatus ? RIL_FORWARDINGSTATUS_DEACTIVATE : RIL_FORWARDINGSTATUS_ACTIVATE), "", 0))
    {
#endif
        szWalk = BeginLineSpecificCommand(szCmd, MAX_ATCMD_LEN, 0);

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,%u", nValue, (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        } else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,%u,,,%u", nValue,
                             (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1), dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

// Generation of an ATD-*#-Code instead of AT+CCWA (if possible)
// Parameters: see RILDrv_GetCallWaitingSettings()
// Return Value:
// TRUE - ATD-string created
// FALSE - No corroponding ATD available
BOOL RILDrv_GenerateATDString4CCWA(DWORD dwInfoClass, LPSTR lpszATDString, DWORD dwStatus)
{
    BOOL bATDavailable = FALSE;
    UINT uiBS;

    uiBS = RILDrv_FindBS4InfoClass(dwInfoClass);
    if (uiBS > 0)
    {
        UINT i;

        for (i = 0; i < NUM_CALLWAITING_VALUES; i++)
        {
            if (g_rgCallWaitingTypeTable[i].dwCallWaitingStatus == dwStatus)
            {
                char szBS[6];

                if (uiBS >= 100)
                    szBS[0] = '\0';
                else
                    _snprintfz(szBS, 6, "%d", uiBS);

                (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s#;\r", g_rgCallWaitingTypeTable[i].szATD, szBS);
                bATDavailable = TRUE;
                break;
            }
        }
    }
    return bATDavailable;
}

//
//
//
static HRESULT ParseGetCallWaitingSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallWaitingSettings);
    UINT i;
    UINT nStatus;
    UINT nValue;
    DWORD* pdwInfoClasses = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwInfoClasses = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwInfoClasses) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwInfoClasses, 0x00, sizeof(DWORD));

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CCWA: "
    while (MatchStringBeginning(szRsp, "+CCWA: ", szRsp)) {
        // Parse "<status>,<class>"
        if (!ParseUInt(szRsp, TRUE, nStatus, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }

        if (1 == nStatus) {
            // Set all required info classes bits
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (nValue & (0x01 << i)) {
                    *pdwInfoClasses |= g_rgdwInfoClasses[i];
                }
            }
        }

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)pdwInfoClasses;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwInfoClasses);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCallWaitingSettings(DWORD dwParam, DWORD dwInfoClasses)
{
    char szCmd[MAX_ATCMD_LEN];
    UINT i;
    DWORD dwClass = 0;

    TBD_FUNCTION(RILDrv_GetCallWaitingSettings);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLWAITING;

    // In order to pass PTCRB/GCF Testcases: 31.3.1.4 & 31.3.1.5 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCWA
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCWA(dwInfoClasses, szCmd, RIL_CALLWAITINGSTATUS_QUERY)) {
#endif
        if (dwInfoClasses == (RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX)) {
            strcpy(szCmd, "AT+CCWA=,2\r");
        }
        else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            sprintf(szCmd, "AT+CCWA=,2,%u\r", dwClass);
        }
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_GETCALLWAITINGSETTINGS, ParseGetCallWaitingSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCallWaitingStatus(DWORD dwParam, DWORD dwInfoClasses, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetCallWaitingStatus);
    UINT i;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLWAITING;

    // Check that the status value is in the correct range
    if (RIL_SVCSTAT_DISABLED != dwStatus && RIL_SVCSTAT_ENABLED != dwStatus) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // In order to pass PTCRB/GCF Testcases: 31.3.1.4 & 31.3.1.5 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCWA
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CCWA(dwInfoClasses, szCmd, RIL_SVCSTAT_DISABLED == dwStatus ? RIL_CALLWAITINGSTATUS_DEACTIVATE:RIL_CALLWAITINGSTATUS_ACTIVATE)) {
#endif
        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CCWA=,%u", (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1));
            szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
        else {
            (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
            szWalk += 2;

            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCWA=,%u,%u", (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1),
                                dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        (void) strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETCALLWAITINGSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
// Send a USSD string.
//
//  lpbData -- points to a Unicode string
//  dwSize  -- size (in bytes) of Unicode string
//
HRESULT RILDrv_SendSupServiceData(DWORD dwParam, const BYTE* lpbData, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SendSupServiceData);
    TBD_ASSERT(NULL != lpbData);
    TBD_ASSERT(0 != dwSize);
    TBD_ASSERT(2 == sizeof(WCHAR)); // for >>1 to substitute /sizeof(WCHAR)

    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    LPSTR szWalk = szCmd;
    DWORD iPort;
    const char pszCUSDPostfix[] = ",15\r"; // 15 = default alphabet, language unspecified
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    iPort = pHandle->GetDevice()->GetPortInUSSDAnswerMode();  // Is there a port in the USSD Answer Mode?

    if (iPort == NUMBER_OF_PORTS) {
        // Use this code for +CSCS settings that don't require us to modify the data bytes (including the one in our init string)
        (void)strncpyz(szWalk, "AT+CUSD=1,", MAX_ATCMD_LEN);
        szWalk = strchr(szCmd, '\0');  // Guaranteed to succeed thanks to _strcpyz
        TBD_ASSERT(0 != szWalk);

        // Convert the Unicode into the current TE character set.
        // This copies directly into the command string buffer.
        if (!AppendQuotedEncodedString(ENCODING_TECHARSET, (LPCWSTR)lpbData, szWalk, &szCmd[MAX_ATCMD_LEN], FALSE))
        {
            hr = E_FAIL;
            goto Error;
        }

        if ( (szWalk + sizeof(pszCUSDPostfix)) > &szCmd[MAX_ATCMD_LEN])
        {
            hr = E_FAIL;
            goto Error;
        }

        // Append the command postfix after the string we just copied
        (void)strncpyz(szWalk, ",15\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

        g_dwLastCUSSDCmd = GetTickCount();
        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (dwSize > 0) {
            _snprintf(szCmd, MAX_ATCMD_LEN, "%S", lpbData);
            pHandle->GetDevice()->LeaveUSSDAnswerMode((COM_PORT_TYPE)iPort, (BYTE *)szCmd, strlen(szCmd));
            // We send a no-operation command to generate an asynchronous response.
            QueueCmd(URC_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr);
        }
        else {
            pHandle->GetDevice()->CancelUSSDAnswerMode((COM_PORT_TYPE)iPort);
            // We send a no-operation command to generate an asynchronous response.
            QueueCmd(URC_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr);
        }
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_CancelSupServiceDataSession(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_CancelSupServiceDataSession);
    HRESULT hr = S_OK;
    INT iPort;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    iPort = pHandle->GetDevice()->GetPortInUSSDAnswerMode();   // Is there a port in the USSD Answer Mode?

    if (iPort == NUMBER_OF_PORTS) {
        if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CUSD=2\r", CMDOPT_NONE, APIID_CANCELSUPSERVICEDATASESSION, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // In USSD answer mode, we have to send an ESC character directly to the COM port.
        pHandle->GetDevice()->CancelUSSDAnswerMode((COM_PORT_TYPE)iPort);
        // We send a no-operation command to generate an asynchronous response.
        QueueCmd(URC_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_CANCELSUPSERVICEDATASESSION, NULL, NULL, hr);
    }

Error:
    return hr;
}
